<?php


namespace chick1993\util;

use ReflectionClass;
use ReflectionException;
use ReflectionMethod;

class Annotation
{
    protected $class = null;
    protected $reflection = null;
    protected $methods = null;
    protected $method = null;

    public function __construct($class)
    {
        if (is_object($class)) {
            $class = get_class($class);
        }
        $this->class = $class;
    }

    static public function init($class):self
    {
        return new static($class);
    }

    /**
     * @return ReflectionClass
     * @throws ReflectionException
     */
    public function getReflection(): ReflectionClass
    {
        if (!($this->reflection instanceof ReflectionClass)) {
            $reflection = new ReflectionClass($this->class);
            $this->reflection = $reflection;
        }
        return $this->reflection;
    }

    /**
     * @param string $name
     * @return ReflectionMethod
     * @throws ReflectionException
     */
    public function getMethod(string $name): ReflectionMethod
    {
        $reflection = $this->getReflection();
        return $reflection->getMethod($name);
    }

    /**
     * @return array
     * @throws ReflectionException
     */
    public function getMethods(): array
    {
        $reflection = $this->getReflection();
        $all_methods = $reflection->getMethods(ReflectionMethod::IS_PUBLIC) ?? [];
        foreach ($all_methods as $val) {
            if ($this->class != $val->class || $val->name == 'initialize') {
                continue;
            }
            if (preg_match('/^__/', $val->name)) {
                continue;
            }
            $this->methods[] = $val;
        }
        $this->methods = $this->methods ?: [];
        return $this->methods;
    }

    /**
     * @return string
     * @throws ReflectionException
     */
    public function getClassDoc(): string
    {
        $reflection = $this->getReflection();
        return $reflection->getDocComment() ?: '';
    }

    /**
     * @return array
     * @throws ReflectionException
     */
    public function getClassDocArray(): array
    {
        return $this->toArray($this->getClassDoc());
    }

    /**
     * @param string $name
     * @return array
     * @throws ReflectionException
     */
    public function getMethodDoc(string $name = ''): array
    {
        if (empty($name)) {
            $methods = $this->getMethods();
            $doc = [];
            foreach ($methods as $method) {
                $doc[$method->getName()] = $method->getDocComment() ?: '';
            }
        } else {
            $method = $this->getMethod($name);
            $doc = [$name => $method->getDocComment()];
        }
        return $doc;
    }

    /**
     * @param string $name
     * @param bool $flag
     * @return array
     * @throws ReflectionException
     */
    public function getMethodDocArray(string $name = '', bool $flag = false): array
    {
        $docs = $this->getMethodDoc($name);
        $temp = [];
        foreach ($docs as $key => $doc) {
            $temp[$key] = $this->toArray($doc);
        }
        if (!empty($name) && true === $flag) {
            return $temp[$name];
        }
        return $temp;
    }

    /**
     * @param string $doc
     * @return array
     */
    protected function toArray(string $doc): array
    {
        $temp = [];
        $preg = '/\*( *@(.*?))* +(.*)/';
        preg_match_all($preg, $doc, $m);
        $keys = $m[2] ?? [];
        $values = $m[3] ?? [];
        foreach ($keys as $k => $key) {
            $val = trim($values[$k] ?? '');
            if (empty($key)) {
                !empty($val) && $temp[] = $val;
            } else {
                if (isset($temp[$key])) {
                    $temp[$key] = (array)$temp[$key];
                    $temp[$key][] = Utils::strToBool($val);
                } else {
                    $temp[$key] = Utils::strToBool($val);
                }
            }
        }
        return $temp;
    }

}